/*
* ====================================================================
* Copyright (c) 2013-2014 Neudesic.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that redistributions of source
* retain the above copyright notice, this list of conditions and the
* following disclaimer.
*
* This source file includes software written by Neudesic, LLC (http://www.neudesic.com).
*
* THIS SOFTWARE IS PROVIDED BY Neudesic, LLC ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  FURTHER, IN NO EVENT SHALL
* Neudesic, LLC (THE AUTHOR) OR ANY CONTRIBUTORS BE LIABLE FOR ANY DAMAGES
* WHATSOEVER THAT ARE RELATED TO THE SOFTWARE [INCLUDING BUT NOT LIMITED TO,
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* SUCH AS LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION, HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)] EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGES.
*
* ====================================================================
*/
#ifndef __SOCKETCONTAINER_H__
#define __SOCKETCONTAINER_H__
//#define _MEASUREWAITTIME
//#define USE_ASYNC_CONNECTANDCLOSE 1

#include "isocketcontainer.h"
#include "AutoLock.h"
#include "winrtsock.h"
#include <windows.networking.sockets.h>
#include "winrtutils.h"
#include "SocketInformation.h"
#include <vector>
#include "HandleManager.h"
#if USE_ASYNC_IO || USE_ASYNC_CONNECTANDCLOSE
# include <windows.system.threading.h>
#endif

#ifdef _MEASUREWAITTIME
#	include "Stopwatch.h"
#endif

using ABI::Windows::Foundation::IClosable;
using ABI::Windows::Foundation::IAsyncOperationCompletedHandler;
using ABI::Windows::Foundation::IAsyncActionCompletedHandler;
using ABI::Windows::Foundation::IAsyncOperationWithProgress;
using ABI::Windows::Networking::IHostName;
using ABI::Windows::Networking::IHostNameFactory;
using ABI::Windows::Networking::HostNameType;
using ABI::Windows::Networking::Sockets::SocketErrorStatus;
using ABI::Windows::Storage::Streams::InputStreamOptions;
using Microsoft::WRL::Wrappers::HStringReference;
using Microsoft::WRL::ComPtr;

namespace Neudesic {
	namespace Sockets {
		template <typename T, typename TInfo>
		class SocketContainer : public ITypedSocketContainer<T, TInfo>
		{
#pragma region Constructors and destructor
		protected:
			SocketContainer(const wchar_t* runtimeClass, SocketType socketType)
				: containerState(SocketContainerState::NotConnected),
				desiredNetworkEvents(0), hWSAEvent(0), readyForEvents(false), enabledEvents(0)
			{
				ComPtr<T> socket;
				HRESULT hr = Windows::Foundation::ActivateInstance(HStringReference(runtimeClass).Get(), &socket);
				if (!::ContinueIfSucceeded(hr, L"Failed to create socket."))
				{
					this->quitEvent.Set();
				}
				this->InitializeContainer(socket, socketType);
			}

			virtual ~SocketContainer()
			{
#if USE_ASYNC_CONNECTANDCLOSE || USE_ASYNC_IO
				//We need to wait for the monitor thread to complete.
				monitorComplete.Wait(TimeoutDuration::Quit);
				monitorComplete.Close();
				connectComplete.Close();
				closeComplete.Close();
#endif
#if USE_ASYNC_IO
				sendComplete.Close();
				receiveComplete.Close();
#endif
				this->connectCompleteWakeup.Close();
				quitEvent.Close();
			}
#pragma endregion Constructors and destructor

#pragma region ISocketContainer interface implementation
		public:
			virtual HRESULT Close()
			{
				if (!this->IsRunning())
					return INVALID_SOCKET;
				this->quitEvent.Set();
				this->containerState = SocketContainerState::Closed;

				//Let the main thread loop know...
#if USE_ASYNC_CONNECTANDCLOSE || USE_ASYNC_IO
				this->closeComplete.Set();
#else
				this->SetNetworkEvent(FD_CLOSE, FD_CLOSE_BIT, 0);
#endif
				ComPtr<IClosable> closable;
				if (SUCCEEDED(this->GetSocket().As(&closable)))
				{
					closable->Close();
				}
				this->GetSocket().Reset();
				return S_OK;
			}

			virtual HRESULT Connect(const struct sockaddr* name, int namelen)
			{
				if (!this->IsRunning())
					return INVALID_SOCKET;
				std::wstring host;
				std::wstring port;
				bool anyPort = this->GetPortAndHost(name, host, port);
				// Create the hostname
				ComPtr<IHostName> hostName;
				ComPtr<IHostNameFactory> hostNameFactory;
				HRESULT hr = ABI::Windows::Foundation::GetActivationFactory(HStringReference(RuntimeClass_Windows_Networking_HostName).Get(), &hostNameFactory);
				if (SUCCEEDED(hr))
				{
					hr = hostNameFactory->CreateHostName(HStringReference(host.c_str()).Get(), &hostName);
					if (SUCCEEDED(hr))
					{
						while (true)
						{
							if (anyPort)
							{
								port = SocketContainer::GetDynamicPort();
							}
							::ShowInfoMessage(L"Trying to connect to [%s] on port [%s] for container [%d] socket [%d].",
								host.c_str(), port.c_str(), this, this->GetSocket());
							hr = this->ConnectSocket(hostName, port.c_str());
							if (SUCCEEDED(hr) || !anyPort)	//If connecting to a specific port, forwward the failure regardless
								break;
							//Getting here means that we are using dynamic ports. If the port is already in use, get another socket.
							if (SocketErrorStatus::SocketErrorStatus_AddressAlreadyInUse != ::GetSocketErrorStatus(hr))
								break;
						}
					}
					SocketContainer::GetHostAndPortInformation(hostName, host, port, &this->connectHostAndPortInfo);
				}
				::ContinueIfSucceeded(hr, L"Connect failed for container [%d] socket [%d].", this, this->GetSocket());
				return hr;
			}

			virtual HRESULT Connect(const struct sockaddr *name, int namelen, LPWSABUF lpCallerData, LPWSABUF lpCalleeData, LPQOS lpSQOS, LPQOS lpGQOS)
			{
				HRESULT hr = E_FAIL;
				if (nullptr == lpCallerData && nullptr == lpSQOS && nullptr == lpGQOS)	//definitely not supported
				{
					hr = Connect(name, namelen);
					if (SUCCEEDED(hr))
					{
						hr = WaitForConnect(TimeoutDuration::Connect);
					}
				}
				::WSASetLastError(FAILED(hr) ? WSAEFAULT : 0);
				return hr;
			}

			virtual HRESULT EventSelect(WSAEVENT hEventObject, long lNetworkEvents)
			{
				if (hEventObject && (hEventObject != this->hWSAEvent))	//Just a sanity check. We don't own the event but will use it.
					::ShowErrorMessage(L"The event object [%d] passed to EventSelect does not match object [%d] event.", hEventObject, this->hWSAEvent);
				AutoLock lock(this->LockObject());
				this->enabledEvents = this->desiredNetworkEvents = lNetworkEvents;
				//::ShowInfoMessage(L"Selected network events [%s] for container [%d] socket [%d].",
				//	this->GetNetworkEventStatus(this->desiredNetworkEvents).c_str(), this, this->GetSocket());
				return S_OK;
			}

			HRESULT GetNetworkEvents(WSAEVENT hEventObject, LPWSANETWORKEVENTS lpNetworkEvents)
			{
				if (nullptr == lpNetworkEvents)
					return E_INVALIDARG;
				::ZeroMemory(lpNetworkEvents, sizeof(WSANETWORKEVENTS));
				if (!this->IsRunning())
					return INVALID_SOCKET;
				AutoLock lock(this->LockObject());

				//We don't own this object so a little protection is necessary. Clearing it hear
				//and showing the network events that have occurred are atomic. It eliminates
				//the need to call WSAResetEvent.
				if (hEventObject)	//Matching what winsock does.
				{
					try
					{
						::ResetEvent(hEventObject);
					}
					catch (...)
					{
						::ShowErrorMessage(L"Exception when calling ResetEvent. Handle must be invalid.");
					}
				}
				//What we are doing here is trying to determine what events are being raised up the chain. We
				//have to look at 3 different things:
				//		Network Events--These are the events raised by the system
				//		Desired Events--These are the events that have been selected using EventSelect
				//		Enabled Events--Events are shut off once raised. When a network event is set, it's re-enabled.
				//
				//Doing a logical AND of these 3 produces a result indicating what will actually be raised.
				long eventsSet = this->networkEvents.lNetworkEvents & this->desiredNetworkEvents & this->enabledEvents;
				//::ShowInfoMessage(L"Network events:[%s]", GetNetworkEventStatus(this->networkEvents.lNetworkEvents).c_str());
				//::ShowInfoMessage(L"Desired events:[%s]", GetNetworkEventStatus(this->desiredNetworkEvents).c_str());
				//::ShowInfoMessage(L"Enabled events:[%s]", GetNetworkEventStatus(this->enabledEvents).c_str());
				//::ShowInfoMessage(L"Raised events:[%s]", GetNetworkEventStatus(eventsSet).c_str());
				for (int i = 0; i < FD_MAX_EVENTS; i++)
				{
					int bit = (1 << i);	//Matches what winsock does to set the FD_XXX bits.
					if (bit & eventsSet)
					{
						lpNetworkEvents->lNetworkEvents |= bit;
						lpNetworkEvents->iErrorCode[i] = this->networkEvents.iErrorCode[i];
						this->enabledEvents ^= bit;	//turns off the event
					}
				}
				//::ShowInfoMessage(L"Enabled events in GetNetworkEvents are [%s] for container [%d] socket [%d].",
				//	this->GetNetworkEventStatus(this->enabledEvents).c_str(), this, this->GetSocket());

				//Now clear our cache so that the same events don't get raised.
				::ZeroMemory(&this->networkEvents, sizeof(WSANETWORKEVENTS));
				return S_OK;
			}

			virtual HRESULT GetPeerName(struct sockaddr* name, int* namelen)
			{
				if (!this->IsRunning())
					return INVALID_SOCKET;

				HRESULT hr = SOCKET_ERROR;
				//Like winsock, bind or connect must have been called. They will have saved the IP and port info.
				if (SocketContainerState::Bound == this->containerState || SocketContainerState::Connected == this->containerState)
				{
					SocketInformation<T, TInfo> si(this->GetSocket());
					HostAndPortInformation info;
					hr = si.GetRemoteHostAndPortInfo(&info);
					if (::ContinueIfSucceeded(hr, L"Failed to get remote host information for container [%d] socket [%d]."))
					{
						hr = this->SetSockAddrInCommon((void*)name, namelen, info.host, info.type, info.port);
					}
				}
				::WSASetLastError(FAILED(hr) ? WSAEFAULT : 0);
				return hr;
			}

			virtual HRESULT GetSocketName(struct sockaddr* name, int* namelen) const = 0;

			SocketType GetSocketType() const
			{
				return this->IsRunning() ? this->socketType : SocketType::InvalidSocket;
			}

			virtual HRESULT GetUnconsumedBufferLength(long command, ULONG* argp)
			{
				//FIONBIO disabling blocking mode and FIONREAD are the only options supported.
				HRESULT hr = (nullptr != argp) ? S_OK : -1;	// a negative number is expected on failure
				if (SUCCEEDED(hr))
				{
					if (FIONREAD == command)
					{
						*argp = 0;
						if (nullptr == this->activeReader)
						{
							::ShowInfoMessage(L"GetUnconsumedBufferLength called without an active datareader for container [%d] socket [%d].", this, this->GetSocket());
						}
						else
						{
							UINT32 length;
							hr = this->activeReader->get_UnconsumedBufferLength(&length);
							if (::ContinueIfSucceeded(hr, L"get_UnconsumedBufferLength failed to read length for container [%d] socket [%d].", this, this->GetSocket()))
							{
								*argp = (ULONG)length;
								//::ShowInfoMessage(L"GetUnconsumedBufferLength returning [%d] bytes for container [%d] socket [%d].",
								//	length, this, this->GetSocket());
							}
						}
					}
					else if ((FIONBIO != command) || ((FIONBIO == command) && !*argp))
						hr = -1; //Blocking mode not supported
				}
				::WSASetLastError(FAILED(hr) ? WSAEFAULT : 0);
				return hr;
			}

			virtual bool IsRunning() const
			{
				return !(WAIT_OBJECT_0 == this->quitEvent.Wait(0));
			}

			virtual HRESULT SetSocketOptions(int level, int optname, const char* optval, int optlen) = 0;

			HRESULT SetWSAEvent(HANDLE hWSAEvent)
			{
				if (!this->IsRunning())
					return INVALID_SOCKET;
				this->hWSAEvent = hWSAEvent;
				return S_OK;
			}

			HRESULT Shutdown(int how)
			{
				HRESULT hr = INVALID_SOCKET;
				if (this->IsRunning())
				{
					this->Close();
				}
				return hr;
			}

		protected:
			virtual HRESULT ConnectSocket(const ComPtr<IHostName>& hostName, const wchar_t* serviceName) = 0;

			virtual HRESULT InternalReceive(char *buf, int bufferLength, int flags, LPDWORD lpNumberOfBytesRecvd)
			{
				if (!this->IsRunning())
					return INVALID_SOCKET;
				HRESULT hr = HRESULT_FROM_WIN32(WSAEFAULT);
				UINT32 lengthToRead = 0;
				if (buf && this->activeReader)
				{
					UINT32 unConsumedLength;
					//Start by getting the amount of data in the buffer.
					hr = this->activeReader->get_UnconsumedBufferLength(&unConsumedLength);
					if (::ContinueIfSucceeded(hr, L"get_UnconsumedBufferLength failed for container[%d] socket[%d].", this, this->GetSocket()))
					{
						//Now only read either whats left or the amount requested. Whichever is smaller.
						lengthToRead = min((UINT32)bufferLength, unConsumedLength);
						hr = this->activeReader->ReadBytes(lengthToRead, (BYTE*)buf);
						//Sanity check
						if (lpNumberOfBytesRecvd)
							*lpNumberOfBytesRecvd = lengthToRead;
						//::ShowInfoMessage(L"InternalReceive of [%d] bytes for container [%d] socket [%d].", lengthToRead, this, this->GetSocket());
						::ContinueIfSucceeded(hr, L"ReadBytes failed to read [%d] bytes for container [%d] socket [%d].", lengthToRead, this, this->GetSocket());
					}
				}
				//Must continue to set this bit. It's what continues the read loop.
#if USE_ASYNC_IO
				if (lengthToRead)
					this->SignalReceiveComplete();
#else
				this->SetNetworkEvent(FD_READ, FD_READ_BIT, hr);
#endif
				return hr;
			}
#pragma endregion ISocketContainer interface implementation

#pragma region ITypedSocketContainer interface implementation
		public:
			ComPtr<T> GetSocket() const
			{
				return this->socket;
			}
#pragma endregion ITypedSocketContainer interface implementation

#pragma region Private implementation functions
		private:
			void InitializeContainer(ComPtr<T> socket, SocketType socketType)
			{
				::ZeroMemory(&this->networkEvents, sizeof(WSANETWORKEVENTS));
				this->socket = socket;
				this->socketType = socketType;

#if USE_ASYNC_CONNECTANDCLOSE || USE_ASYNC_IO
				SetupEventMonitor();
#endif
			}

#if USE_ASYNC_CONNECTANDCLOSE || USE_ASYNC_IO
			HRESULT SetupEventMonitor()
			{
				ComPtr<ABI::Windows::System::Threading::IThreadPoolStatics> threadPool;
				HRESULT hr = ABI::Windows::Foundation::GetActivationFactory(HStringReference(RuntimeClass_Windows_System_Threading_ThreadPool).Get(), &threadPool);
				ComPtr<IAsyncAction> asyncAction;
				hr = threadPool->RunAsync(::Callback<ABI::Windows::System::Threading::IWorkItemHandler>([this](IAsyncAction* asyncAction) -> HRESULT
				{
					bool runForever = true;
					while (runForever)
					{
						//UINT numberOfBytes;
						HRESULT hrComplete = S_OK;
						HANDLE waitHandles[] = { this->quitEvent.Get(), this->connectComplete.Get(), this->closeComplete.Get()
#if USE_ASYNC_IO
							, this->sendComplete.Get(), this->receiveComplete.Get()
#endif
						};
						int result = ::WaitForMultipleObjectsEx(sizeof(waitHandles) / sizeof(HANDLE), waitHandles, 0, INFINITE, 0);
						//If the first event signals, we're in the process of closing.
						switch (result)
						{
						case WAIT_OBJECT_0:
							runForever = false;
							this->monitorComplete.Set();
							break;
						case WAIT_OBJECT_0 + 1:
							hrComplete = this->connectAsyncAction->GetResults();	//Per the documentation this always returns S_OK!!! Really???
							if (::ContinueIfSucceeded(hrComplete, L"Getting Connect results reported failure for container [%d] socket [%d].", this, this->GetSocket()))
							{
								::ShowInfoMessage(L"Connected to container [%d] socket [%d] with result [%d].", this, this->GetSocket(), result);
								this->containerState = SocketContainerState::Connected;
							}
							this->connectCompleteWakeup.Set();
							//FD_CONNECT and FD_WRITE are always raised together.
							this->SetNetworkEvent(FD_CONNECT | FD_WRITE, FD_CONNECT_BIT, hrComplete);
							break;
						case WAIT_OBJECT_0 + 2:
							this->SetNetworkEvent(FD_CLOSE, FD_CLOSE_BIT, hrComplete);
							break;
#if USE_ASYNC_IO
						case WAIT_OBJECT_0 + 3:
							//hrComplete = this->sendAsyncOperation->GetResults(&numberOfBytes);
							//::ContinueIfSucceeded(hrComplete, L"GetResults failed for container [%d] socket [%d].", this, this->GetSocket());
							//::ShowInfoMessage(L"Number of bytes sent is &d for container [%d] socket [%d].", numberOfBytes, this, this->GetSocket());
							this->SetNetworkEvent(FD_WRITE, FD_WRITE_BIT, hrComplete);
							break;
						case WAIT_OBJECT_0 + 4:
							//Start by getting the amount of data in the buffer.
							//hrComplete = this->activeReader->get_UnconsumedBufferLength(&numberOfBytes);
							//::ContinueIfSucceeded(hrComplete, L"get_UnconsumedBufferLength failed for container [%d] socket [%d].", this, this->GetSocket());
							//::ShowInfoMessage(L"Number of bytes received is [%d] for container [%d] socket [%d].", numberOfBytes, this, this->GetSocket());
							this->SetNetworkEvent(FD_READ, FD_READ_BIT, hrComplete);
							break;
#endif
						};
						::Sleep(500);
					};
					return S_OK;
				}).Get(), &asyncAction);
				return S_OK;
			}
#endif

			HANDLE GetConnectCompleteHandle() const
			{
				//This event is set by the async event handler. The original event must be reset. This one will
				//remain signaled.
				return this->connectCompleteWakeup.Get();
			}

#if USE_ASYNC_CONNECTANDCLOSE || USE_ASYNC_IO
			void SignalConnectionComplete()
			{
				this->connectComplete.Set();
			}
#endif

#pragma endregion Private implementation functions

#pragma region Container helper functions
		protected:
			ComPtr<IAsyncAction> GetConnectAction() const
			{
				return connectAsyncAction;
			}

#if USE_ASYNC_IO
			void SignalSendCompletion()
			{
				this->sendComplete.Set();
			}

			void SignalReceiveComplete()
			{
				this->receiveComplete.Set();
			}
#else
			void SetConnectAction(ComPtr<IAsyncAction> connectAsyncAction)
			{
				this->connectAsyncAction = connectAsyncAction;
			}

			void SetConnectComplete()
			{
				this->connectCompleteWakeup.Set();
			}
#endif
			SocketContainerState GetContainerState() const
			{
				return this->containerState;
			}

			void SetContainerState(SocketContainerState state)
			{
				this->containerState = state;
			}

			static HRESULT GetHostAndPortInformation(const ComPtr<IHostName> hostName, const std::wstring host, const std::wstring port, HostAndPortInformation *info)
			{
				HSTRING hCName;
				HRESULT hr = hostName->get_CanonicalName(&hCName);
				if (SUCCEEDED(hr))
				{
					info->host = ::ConvertToWideString(&hCName);
					::WindowsDeleteString(hCName);
					hr = hostName->get_Type(&info->type);
					info->port = port;
					if (host != info->host)
					{
						::ShowErrorMessage(L"GetHostAndPortInformation canonical name [%s] host name [%s] mismatch.",
							info->host.c_str(), host.c_str());
					}
				}
				return hr;
			}

			static std::wstring GetDynamicPort()
			{
				//These numbers  represent the dynamic range of ports for TCP/IP as defined by the following articles:
				//		http://support.microsoft.com/kb/929851
				//		http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers
				static const int dynamicMin = 49152;
				static const int dynamicMax = 65535;
				int dynamicPort = (int)((double)::rand() / (RAND_MAX + 1) * (dynamicMax - dynamicMin)) + dynamicMin;
				wchar_t wPort[10];
				::swprintf_s(wPort, (sizeof(wPort) / sizeof(wchar_t)), L"%d", dynamicPort);
				return std::wstring(wPort);
			}

			static HRESULT SetSockAddrInCommon(void* vsai, int* namelen, const std::wstring& canonicalName, HostNameType hostNameType, const std::wstring& port)
			{
				std::string mbCanonicalName = ::ConvertToMultiByteString(canonicalName);
				std::string mbPort = ::ConvertToMultiByteString(port);
				::ZeroMemory(vsai, *namelen);
				//Need to convert network byte order.
				USHORT htonsPort = ::htons((USHORT)::strtoul(mbPort.c_str(), NULL, 0));
				int error = WSAEFAULT;
				if (hostNameType == HostNameType::HostNameType_Ipv4)
				{
					error = ValidateLengthRequest(AF_INET, namelen);
					if (!error)
					{
						*namelen = sizeof(sockaddr_in);
						sockaddr_in* sa4 = (sockaddr_in*)vsai;
						sa4->sin_family = AF_INET;
						::inet_pton(AF_INET, mbCanonicalName.c_str(), &(sa4->sin_addr));
						sa4->sin_port = htonsPort;
					}
				}
				else if (hostNameType == HostNameType::HostNameType_Ipv6)
				{
					error = ValidateLengthRequest(AF_INET6, namelen);
					if (!error)
					{
						*namelen = sizeof(sockaddr_in6);
						sockaddr_in6* sa6 = (sockaddr_in6*)vsai;
						sa6->sin6_family = AF_INET6;
						::inet_pton(AF_INET6, mbCanonicalName.c_str(), &(sa6->sin6_addr));
						sa6->sin6_port = htonsPort;
					}
				}
				else
				{
					::ShowErrorMessageWithResult(error, L"HostNameType [%d] is invalid. Canonical name [%s] Port [%s]",
						hostNameType, canonicalName.c_str(), port.c_str());
				}
				return HRESULT_FROM_WIN32(error);
			}

			HRESULT WaitForConnect(int timeout)
			{
				if (!IsRunning())
					return SOCKET_ERROR;

				//The StreamSocketContainer Select may call this function twice depending on how
				//the winsock select is used--once for reads and the other for writes. We want
				//to make certain that we wait for connect just once but only after the system
				//has set the desired events.
				if (this->desiredNetworkEvents && this->readyForEvents)
					return S_OK;
				//Connect works differently for datagram and stream sockets. For datagram sockets it merely record the peers address. On
				//the other hand a stream socket establishes an end-to-end connect.
				//
				//NEVER block waiting for connect to complete a datagram socket. It will always timeout!
				int result = (SocketType::DatagramSocketType == this->GetSocketType())
					? 0
					: this->WaitForEvent(this->GetConnectCompleteHandle(), timeout, L"ConnectComplete");
				if (!this->IsRunning())	//Sanity check. The socket may have been closed while waiting.
					return SOCKET_ERROR;

				HRESULT hr = S_OK;
				if (WAIT_TIMEOUT == result)
				{
					::ShowErrorMessage(L"Timeout waiting for ConnectAsync for container [%d] socket [%d].", this, this->GetSocket());
					hr = HRESULT_FROM_WIN32(result);
				}
				if (SUCCEEDED(hr))
				{
					this->containerState = SocketContainerState::Connected;
				}

				//::ShowInfoMessage(L"Connect attempt to container [%d] socket [%d] with result [%d]. Socket state [%d].",
				//	this, this->GetSocket(), result, this->containerState);
				//This check insures that we will not raise the FD_CONNECT on the same socket more than once.
				if (this->desiredNetworkEvents)
				{
					this->readyForEvents = true;
					if (SUCCEEDED(hr))
					{
						//Datagram sockets don't fire a connect event.
						if (SocketType::StreamSocketType == this->GetSocketType())
						{
							this->SetNetworkEvent(FD_CONNECT | FD_WRITE, FD_CONNECT_BIT, hr);
							hr = this->connectAsyncAction->GetResults();	//Per the documentation this always returns S_OK!!! Really???
							::ContinueIfSucceeded(hr, L"Connect GetResults failed for container [%d] socket [%d].", this, this->GetSocket());
						}
					}
				}
				return hr;
			}

			void PrepareDataReader(ComPtr<IDataReader>& activeReader)
			{
				this->activeReader = activeReader;
			}

			int WaitForEvent(HANDLE hEvent, int timeout, wchar_t* waitType)
			{
#ifdef _MEASUREWAITTIME
				watch.Start();
				::ShowInfoMessage(L"Waiting for up to [%f] seconds on [%s] event for container [%d] socket [%d].",
					timeout/1000.0, waitType, this, this->GetSocket());
#endif
				const HANDLE hEvents[2] = { this->quitEvent.Get(), hEvent };
				int result = EventManager::WaitAll(sizeof(hEvents) / sizeof(HANDLE), hEvents, 0, timeout);
#ifdef _MEASUREWAITTIME
				watch.Stop();
				auto time = watch.GetElapsedTime();
				//if (time > .500)
				::ShowInfoMessage(L"Waited %f seconds for [%s] event for container [%d] socket [%d]. Result = [%d/0x%x]",
					watch.GetElapsedTime(), waitType, this, this->GetSocket(), result, result);
#endif
				//Will return 1 if the event signals. 0 = quit, 258 = WAIT_TIMEOUT
				return result;
			}

			HRESULT SetNetworkEvent(long lNetworkEvents, UINT errorIndex, int errorCode)
			{
				//Events do not get fired here. We merely set them for retrieval later.
				if (!this->IsRunning())
					return INVALID_SOCKET;

				//This logic matches what winsock does per the documentation. Recording begins after
				//WSAEventSelect has been called to set the desired events. Until that time, no events
				//are recorded.
				AutoLock lock(this->LockObject());
				if (this->desiredNetworkEvents)		//Shoule we record the message?
				{
					this->networkEvents.lNetworkEvents |= lNetworkEvents;
					this->networkEvents.iErrorCode[errorIndex] = errorCode;

					//An unblocking event occurred. Re-enable the events that are being raised. GetNetworkEvents
					//disable events that are being raised for the main thread. Here we re-enable the event. This
					//will keep us from flooding the application with events.
					/*
						Event		Reenabling function
						=====		===================
						FD_READ		recv, recvfrom, WSARecv, or WSARecvFrom
						FD_WRITE	send, sendto, WSASend, or WSASendTo
						FD_ACCEPT	accept or WSAAccept
						FD_CONNECT	None
						FD_CLOSE	None
						*/

					this->enabledEvents |= lNetworkEvents;	//Re-enable fired events.
					//::ShowInfoMessage(L"Enabled events in SetNetworkEvent are %s for container [%d] socket [%d].",
					//	this->GetNetworkEventStatus(this->enabledEvents).c_str(), this, this->GetSocket());

					//We must set this event in order for the main thread to know that an event has completed and
					//needs to wakeup. Since we don't own this object a little protection is prudent.
					if (this->hWSAEvent)
					{
						try
						{
							//::ShowInfoMessage(L"SetNetworkEvent signaled event [%d] for container [%d] socket [%d].",
							//	this->hWSAEvent, this, this->GetSocket());
							::SetEvent(this->hWSAEvent);
						}
						catch (...)
						{
							::ShowErrorMessage(L"Exception thrown when setting hWSAEvent. Handle must be invalid.");
						}
					}
				}
				return S_OK;
			}

			CriticalSection LockObject()
			{
				return this->lockObject;
			}

			bool GetPortAndHost(const sockaddr* sa, std::wstring& outHost, std::wstring& outPort) const
			{
				sockaddr_in* sa4 = (sockaddr_in*)sa;
				sockaddr_in6* sa6 = (sockaddr_in6*)sa;

				//This should be safe to do...
				int family = sa4->sin_family;
				char tmp[NI_MAXHOST];
				if (AF_INET == family)
				{
					::inet_ntop(AF_INET, &sa4->sin_addr, tmp, NI_MAXHOST);
				}
				else
				{
					::inet_ntop(AF_INET6, &sa6->sin6_addr, tmp, NI_MAXHOST);
				}
				wchar_t wstrHost[NI_MAXHOST];
				::ConvertToWideString(tmp, (sizeof(wstrHost) / sizeof(wchar_t)), wstrHost);
				outHost = wstrHost;

				//Network format is big endian. Need to convert back to little endian.
				USHORT port = ::ntohs((family == AF_INET6) ? sa6->sin6_port : sa4->sin_port);
				wchar_t wPort[10];
				::ZeroMemory(wPort, sizeof(wPort));
				if (port)
					::swprintf_s(wPort, (sizeof(wPort) / sizeof(wchar_t)), L"%d", port);
				outPort = wPort;
				return port ? false : true;
			}
#pragma endregion Container helper functions

#pragma region Private helper functions
		private:
//#ifdef _DEBUG
//			static std::wstring GetNetworkEventStatus(long lNetworkEvents)
//			{
//				std::wstring eventString = L"(";
//				if (lNetworkEvents & FD_READ)
//					eventString += L" FD_READ";
//				if (lNetworkEvents & FD_WRITE)
//					eventString += L" FD_WRITE";
//				if (lNetworkEvents & FD_CLOSE)
//					eventString += L" FD_CLOSE";
//				if (lNetworkEvents & FD_ACCEPT)
//					eventString += L" FD_ACCEPT";
//				if (lNetworkEvents & FD_CONNECT)
//					eventString += L" FD_CONNECT";
//				if (eventString.length() <= 1)
//					eventString += L" no events";
//				eventString += L" )";
//				return eventString;
//			}
//#endif
			static int ValidateLengthRequest(int type, int* namelen)
			{
				int error = WSAEFAULT;
				if (nullptr == namelen)
				{
					::ShowErrorMessageWithResult(error, L"namelen argument must not be null.");
				}
				else if (*namelen > sizeof(sockaddr_storage))
				{
					::ShowErrorMessageWithResult(error, L"namelen argument must not be greater than size of sockaddr_storage.");
				}
				else if (AF_INET == type &&  *namelen < sizeof(sockaddr_in))
				{
					::ShowErrorMessageWithResult(error, L"namelen argument must not be smaller than size of sockaddr_in.");
				}
				else if (AF_INET6 == type &&  *namelen < sizeof(sockaddr_in6))
				{
					::ShowErrorMessageWithResult(error, L"namelen argument must not be smaller than size of sockaddr_in6.");
				}
				else
					error = 0;
				return error;
			}
#pragma endregion Private helper functions

#pragma region Container state
		private:
			SocketContainerState containerState;
			ComPtr<T> socket;
			SocketType socketType;
			//These events are manual reset events because we want them to remain signaled once set.
			ManualResetEvent connectCompleteWakeup;
			ManualResetEvent quitEvent;
#if USE_ASYNC_CONNECTANDCLOSE || USE_ASYNC_IO
			ManualResetEvent monitorComplete;
			//These events are auto reset events. They will signal, wake up the monitor thread, then
			//automatically reset. They are only used by the monitor thread.
			AutoResetEvent connectComplete;
			AutoResetEvent closeComplete;
#endif
#if USE_ASYNC_IO
			AutoResetEvent sendComplete;
			AutoResetEvent receiveComplete;
#endif
			ComPtr<IAsyncAction> connectAsyncAction;
			ComPtr<IDataReader> activeReader;
			CriticalSection lockObject;
			bool readyForEvents;
			//Winsock abstraction stuff
			WSAEVENT hWSAEvent;
			WSANETWORKEVENTS networkEvents;
			long desiredNetworkEvents;
			long enabledEvents;
			HostAndPortInformation connectHostAndPortInfo;
#ifdef _MEASUREWAITTIME
			Stopwatch watch;
#endif
#pragma endregion Container state
		};
	}	//Sockets
}	//Neudesic

#endif